<?php

if (!defined('BASEPATH'))
    exit('No direct script access allowed');

/**
 * Parent Model for performing CRUD operations.
 * @package core
 * @category Model
 * 
 * @author Mark Angelo Angulo
 * @copyright (c) 2015
 */
class Crud extends CI_Model {

    /**
     * @var string
     * Name of the database table to be manipulated. 
     */
    var $table          = '';
    
    /**
     * @var string
     * Primary key of the table. 
     */
    var $primary_key    = 'id';
    
    /**
     * Result array container
     * @var array
     */
    var $result         = array();
    
    var $limit          = NULL;
    var $offset         = NULL;
    var $order          = array();

    /**
     * Constructor Method.
     */
    function __construct()
    {
        parent::__construct();
        $this->load->database();
        $this->load->library('datatables'); 
    }

    /**
     * Limit query records
     * @param int $limit number of records to be retrieved
     * @param int $offset number of records to be omitted
     * @return \Crud
     */
    function limit( $limit, $offset = NULL)
    {
        $this->limit        = is_numeric($limit)    ? $limit    : NULL;
        $this->offset       = is_numeric($offset)   ? $offset   : NULL;
        return $this;
    }
    
    /**
     * Sort query records
     * @param string $order field sort
     * @param string $sort asc or desc
     * @return \Crud
     */
    function order_by( $order, $sort = 'asc' )
    {
        $orders             = $this->order;
        $orders[$order]     = $sort;
        $this->order        = $orders;
        
        return $this;
    }
    
    /**
     * Combine limit, offset and sorting
     * @param int $limit number of records to be retrieved
     * @param int $offset number of records to be omitted
     * @param string $order field sort
     * @param string $sort asc or desc
     * @return \Crud2
     */
    function filter($limit = NULL, $offset = NULL, $order = NULL, $sort = 'asc')
    {
        $this->limit        = is_numeric($limit)    ? $limit    : NULL;
        $this->offset       = is_numeric($offset)   ? $offset   : NULL;
        
        $orders             = $this->order;
        $orders[$order]     = $sort;
        $this->order        = $orders;
        
        return $this;
    }

    /**
     * Inserts a value to the database
     * @access public
     * @param array $data Associative Array containing information of the data being inserted to the database.
     * @return int Returns the id of the newly inserted row in the database.
     */
    function insert($data)
    {
        $this->db->insert($this->table, $data);
        return $this->db->insert_id();
    }
    
//    function insert($data)
//    {
//        $this->db->insert($this->table, $data);
//        $error_msg = $this->db->_error_message();
//        
//        // if we cannot add a record return null
//        return $error_msg ? $this->db->insert_id() : NULL;
//    }
    
    /**
     * Retrive a certain record. Create it if it doesn't exist.
     * @access public
     * @param array $args An array containing filters to the database query.
     * @param array $record An array containing the information about the record.
     * @return array Returns the record.
     */
    public function push($args = array(), $record = array())
    {
        $this->db->where($args);
        $data = $this->db->get($this->table)->row_array();

        if ( empty($data) )
        {
            // if it doesn't exist, we create it.
            $id = $this->insert($record);
            return $id;
        } else
        {
            // otherwise return its ID.
            return $data[$this->primary_key];
        }
    }

    /**
     * Retrieves one record from a database table using its id.
     * @access public
     * @param int $id The id of the content being retrieved from the database.
     * @param string/array $aliases Comma Separated database attributes same to datatables header to be retrieved.
     * @return array|null Returns the row from the database as an array if id is found, otherwise null.
     */
    function find($id, $aliases = '*')
    {
        if( $id == NULL )
        {
            $this->result = array();
            return $this;
        } else
        {

            if(is_array($aliases))
            {
                $aliases_string = '';
                foreach ($aliases as $key => $alias)
                {
                    end($aliases);
                    if ($key === key($aliases))
                    {
                        $aliases_string .= $alias . ' ';
                    } else
                    {
                        $aliases_string .= $alias . ', ';
                    }
                }
                $aliases = $aliases_string;
            }
            
            $this->db->select($aliases, FALSE);
            $this->db->where($this->primary_key, $id);
            $query = $this->db->get($this->table);

            $result = $query->result_array();
            $this->result = (count($result) > 0 ? $result[0] : array());
            return $this;
        }
    }

    /**
     * Retrieves one record from a database table using specific filters.
     * @access public
     * @param array $args Associative Array containing filters to the database query.
     * @param string $alias Comma Separated database attributes same to datatables header to be retrieved
     * @return array|null Returns the row from the database as an array if found, otherwise null.
     */
    function one($args = array(), $alias = '*')
    {
        if (empty($args))
        {
            $this->result = array();
            return $this;
        } else
        {
            $this->db->select($alias, FALSE);
            $this->db->where($args);
            $query = $this->db->get($this->table);

            $result = $query->result_array();
            $this->result =  (count($result) > 0 ? $result[0] : array());
            return $this;
        }
    }
    
    /**
     * Retrieves record's specific attribute from a database table using its id.
     * @access public
     * @param array $id ID of the record from which attribute is retrieved.
     * @param string $column The column/attribute being retrieved.
     * @return array|null Returns the value of the column/attribute if found, otherwise null.
     */
    function find_attribute($id = NULL, $column = NULL)
    {
        if ( $id == NULL OR $column == NULL )
        {
            $this->result = array();
            return $this;
        } else
        {
            $this->db->where($this->primary_key, $id);
            $query = $this->db->get($this->table);

            $result = $query->row_array();
            $this->result = (empty($result) ? NULL : $result[$column]);
            return $this;
        }
    }
    
    /**
     * Retrieves record's specific attribute from a database table with certain filters.
     * @access public
     * @param array $args An array containing filters to the database query.
     * @param string $column The column/attribute being retrieved.
     * @return array|null Returns the value of the column/attribute if found, otherwise null.
     */
    function attribute($args = array(), $column = NULL)
    {
        if ( empty($args) OR $column == NULL )
        {
            $this->result = array();
            return $this;
        } else
        {
            $this->db->where($args);
            $query = $this->db->get($this->table);

            $result = $query->row_array();
            $this->result = (empty($result) ? NULL : $result[$column]);
            return $this;
        }
    }

    /**
     * Retrieves an array of specific column from a database table.
     * @access public
     * @param array $args An array containing filters to the database query.
     * @param string $column The database column values to be retrieved.
     * @param int $offset Starting point of the records to be retrieved.
     * @param int $limit Number of records to be retrieved. Default is NULL or all records.
     * @param string $sort The name of the column where the records will be sorted example 'id'.
     * @param string $order Order of the records being retrieved values should either be 'asc' or 'desc'.
     * @return array Returns an array of filtered records.
     */
    function column($args = array(), $column = NULL, $limit = NULL, $offset = 0, $sort = NULL, $order = 'asc')
    {
        if ( empty($column) OR $column == NULL )
        {
            $this->result = array();
            return $this;
        } else
        {
            $limit      = $this->limit      === NULL ? $limit   : $this->limit;
            $offset     = $this->offset     === NULL ? $offset  : $this->offset;
            $sort       = $sort             === NULL ? $column  : $sort;

            // sort and orders
            $orders     = $this->order;
            if( $sort != $column )
            {
                $orders[$sort] = $order;
            }
            if( count($orders) > 0 )
            {
                foreach($orders as $key => $ord)
                {
                    $this->db->order_by($key, $ord);
                }
            } else
            {
                $this->db->order_by($sort, $order);
            }
            
            if (count($args) > 0)
            {
                $this->db->where($args);
            }

            $query = $this->db->get($this->table, $limit, $offset);

            $column_values = array();
            foreach ($query->result_array() as $query)
            {
                array_push($column_values, $query[$column]);
            }
            $this->result = $column_values;
            return $this;
        }
    }
    
    /**
     * Retrieves an array of specific column from a database table within a set of records.
     * @access public
     * @param array $arr A set of values to use as filter.
     * @param string $column The database column values to be retrieved.
     * @param string $column_select The database column to be compared from.
     * @param int $offset Starting point of the records to be retrieved.
     * @param int $limit Number of records to be retrieved. Default is NULL or all records.
     * @param string $sort The name of the column where the records will be sorted example 'id'.
     * @param string $order Order of the records being retrieved values should either be 'asc' or 'desc'.
     * @return array Returns an array of filtered records.
     */
    function column_in($args = array(), $arr = array(), $column = NULL, $column_select = NULL, $limit = NULL, $offset = 0, $sort = NULL, $order = 'asc')
    {
        $column_select = $column_select === NULL ? $this->primary_key : $column_select;
        
        if (empty($column) OR $column == NULL OR empty($arr) OR $arr == NULL)
        {
            return NULL;
        } else
        {
            $limit      = $this->limit      === NULL ? $limit       : $this->limit;
            $offset     = $this->offset     === NULL ? $offset      : $this->offset;
            $sort       = $sort             === NULL ? $column      : $sort;

            // sort and orders
            $orders     = $this->order;
            if( $sort != $column )
            {
                $orders[$sort] = $order;
            }
            if( count($orders) > 0 )
            {
                foreach($orders as $key => $ord)
                {
                    $this->db->order_by($key, $ord);
                }
            } else
            {
                $this->db->order_by($sort, $order);
            }
            
            if( count($arr) > 0)
            {
                $this->db->where_in($column_select, $arr);
            }
            
            if( count($args) > 0)
            {
                $this->db->where($args);
            }

            $query = $this->db->get($this->table, $limit, $offset);

            $column_values = array();
            foreach ($query->result_array() as $query)
            {
                array_push($column_values, $query[$column]);
            }
            return $column_values;
        }
    }
    
    /**
     * Retrieves limited number of records from a database table.
     * @access public
     * @param array $args An array containing filters to the database query.
     * @param string $alias Comma Separated database attributes same to datatables header to be retrieved.
     * @param int $offset Starting point of the records to be retrieved.
     * @param int $limit Number of records to be retrieved. Default is NULL or all records.
     * @param string $sort The name of the column where the records will be sorted example 'id'.
     * @param string $order Order of the records being retrieved values should either be 'asc' or 'desc'.
     * @return array Returns an array of limited records.
     */
    function all_api($args = array(), $alias = '*', $limit = NULL, $offset = 0, $sort = NULL, $order = 'asc', $tables = NULL)
    {
        // check sort fields
        $sort_fields = $sort === NULL ? $this->primary_key : trim($sort);
        $sorters = explode(',', $sort_fields);
        foreach($sorters as $sorter)
        {
            $sorter = changeCase( trim($sorter), 'snake' );
            if( $this->db->field_exists( $sorter, $this->table) )
            {
                $this->db->order_by($sorter, $order);
            } else
            {
                format_response('error', 400, 'Invalid Sort Field', array( 'sort' => $sorter . ' field doesn\'t exist' ));
                break;
            }
        }
        
        // check table attributes
        $field_aliases = '';
        $aliases = explode(',', $alias);
        foreach($aliases as $field)
        {
            $field = trim($field);
            $fields = explode(' ', $field);
            if( $fields[0] != '*' && ! $this->db->field_exists( changeCase($fields[0], 'snake'), $this->table) )
            {
                format_response('error', 400, 'Invalid Field', array( 'field' => $fields[0] . ' field doesn\'t exist' ));
                break;
            } else
            {
                $fields[0] = changeCase($fields[0], 'snake');
                $new_field = implode(' ', $fields);
                $field_aliases .= $new_field . ',';
            }
        }
        
        $this->db->select($field_aliases, FALSE);
        
        // filter query if arguments exists
        if (count($args) > 0)
        {
            foreach ( $args as $key => $arg )
            {
                if( $this->db->field_exists($key, $this->table) )
                {
                    $this->db->where(urldecode($key), urldecode($arg));
                } else
                {
                    format_response('error', 400, 'Invalid Parameter', array( 'field' => $key . ' field doesn\'t exist' ));
                    break;
                }
            }
        }
        
        if($tables != NULL)
        {
            foreach ($tables as $key => $table)
            {
                $this->db->join($key, $table);
            }
        }
        
        $query = $this->db->get($this->table, $limit, $offset);
        $this->result = $query->result_array();
        return $this;
    }

    /**
     * Retrieves limited number of records from a database table.
     * @access public
     * @param array $args An array containing filters to the database query.
     * @param string $alias Comma Separated database attributes same to datatables header to be retrieved.
     * @param int $offset Starting point of the records to be retrieved.
     * @param int $limit Number of records to be retrieved. Default is NULL or all records.
     * @param string $sort The name of the column where the records will be sorted example 'id'.
     * @param string $order Order of the records being retrieved values should either be 'asc' or 'desc'.
     * @return array Returns an array of limited records.
     */
    function all($args = array(), $alias = '*', $limit = NULL, $offset = 0, $sort = NULL, $order = 'asc', $tables = NULL)
    {
        $limit      = $this->limit      === NULL ? $limit               : $this->limit;
        $offset     = $this->offset     === NULL ? $offset              : $this->offset;
        $sort       = $sort             === NULL ? $this->primary_key   : $sort;
        
        // sort and orders
        $orders     = $this->order;
        if( $sort != $this->primary_key )
        {
            $orders[$sort] = $order;
        }
        if( count($orders) > 0 )
        {
            foreach($orders as $key => $ord)
            {
                $this->db->order_by($key, $ord);
            }
        } else
        {
            $this->db->order_by($sort, $order);
        }
        
        $this->db->select($alias, FALSE);
        
        // filter query if arguments exists
        if (count($args) > 0)
        {
            $this->db->where($args);
        }
        
        if($tables != NULL)
        {
            foreach ($tables as $key => $table)
            {
                $this->db->join($key, $table);
            }
        }
        
        $query = $this->db->get($this->table, $limit, $offset);
        $this->result = $query->result_array();
        return $this;
    }
    
    /**
     * Retrieves limited number of records from a database table within a set of records.
     * @access public
     * @param array $arr A set of values to use as filter.
     * @param string $alias Comma Separated database attributes same to datatables header to be retrieved.
     * @param int $offset Starting point of the records to be retrieved.
     * @param int $limit Number of records to be retrieved. Default is NULL or all records.
     * @param string $sort The name of the column where the records will be sorted example 'id'.
     * @param string $order Order of the records being retrieved values should either be 'asc' or 'desc'.
     * @return array Returns an array of limited records.
     */
    function all_in($args = array(), $arr = array(), $column_select = NULL, $alias = '*', $limit = NULL, $offset = 0, $sort = NULL, $order = 'asc', $tables = NULL)
    {
        $limit              = $this->limit      === NULL ? $limit               : $this->limit;
        $offset             = $this->offset     === NULL ? $offset              : $this->offset;
        $sort               = $sort             === NULL ? $this->primary_key   : $sort;
        $column_select      = $column_select    === NULL ? $this->primary_key   : $column_select;
        
        // sort and orders
        $orders     = $this->order;
        if( $sort != $this->primary_key )
        {
            $orders[$sort] = $order;
        }
        if( count($orders) > 0 )
        {
            foreach($orders as $key => $ord)
            {
                $this->db->order_by($key, $ord);
            }
        } else
        {
            $this->db->order_by($sort, $order);
        }
        
        $this->db->select($alias, FALSE);

        if( count($arr) > 0)
        {
            $this->db->where_in($column_select, $arr);
        }

        if( count($args) > 0)
        {
            $this->db->where($args);
        }

        if($tables != NULL)
        {
            foreach ($tables as $key => $table)
            {
                $this->db->join($key, $table);
            }
        }

        $query = $this->db->get($this->table, $limit, $offset);
        $this->result = $query->result_array();
        return $this;
    }
    
    /**
     * Retrieves datatable formatted records.
     * @param string $alias Comma Separated database attributes same to datatables header.
     * @param array $args An array containing filters to the database query.
     * @param array $custom_column An array of columns to be customized.
     * @param boolean $checkbox TRUE if checkbox for each data should be generated.
     * @param boolean $options TRUE if edit and delete buttons should be generated.
     * @return json Returns json formatted datatable data.
     */
    function all_datatable($args = array(), $alias = '*', $custom_column = array(), $options = TRUE, $checkbox = TRUE, $tables = NULL)
    {
        $this->datatables->select($alias, FALSE);

        // join tables
        if($tables != NULL)
        {
            foreach ($tables as $key => $table)
            {
                $this->datatables->join($key, $table);
            }
        }

        // filter query if arguments exists
        if (count($args) > 0)
        {
            $this->datatables->where($args);
        }

        // add markup to columns
        if(count($custom_column) > 0)
        {
            foreach ($custom_column as $key => $column)
            {
                $this->datatables->edit_column($key, $column['markup'], $column['keys']);
            }
        }

        // create checkboxes for each data
        if($checkbox)
        {
            $this->datatables->edit_column($this->primary_key,'<input type="checkbox" name="records[]" class="record" value="$1">', 'id');
        } else
        {
            $this->datatables->unset_column($this->primary_key);
        }

        // create buttons for data manipulation
        if($options)
        {
            $this->datatables->add_column('options', $this->options_markup, $this->primary_key);
        }

        $this->datatables->from($this->table);
        return $this->datatables->generate();
    }
    
    /**
     * Retrieves datatable formatted records.
     * @param string $alias Comma Separated database attributes same to datatables header.
     * @param array $args An array containing filters to the database query.
     * @param array $custom_column An array of columns to be customized.
     * @param boolean $checkbox TRUE if checkbox for each data should be generated.
     * @param boolean $options TRUE if edit and delete buttons should be generated.
     * @return json Returns json formatted datatable data.
     */
    function all_in_datatable($args = array(), $arr = array(), $column_select = NULL, $alias = '*', $custom_column = array(), $options = TRUE, $checkbox = TRUE, $tables = NULL)
    {
        $column_select = $column_select === NULL ? $this->primary_key : $column_select;
        
        $this->datatables->select($alias, FALSE);
        
        if( count($arr) > 0)
        {
            $this->db->where_in($column_select, $arr);
        }
        
        // join tables
        if($tables != NULL)
        {
            foreach ($tables as $key => $table)
            {
                $this->datatables->join($key, $table);
            }
        }

        // filter query if arguments exists
        if ( count($args) > 0 )
        {
            $this->datatables->where($args);
        }

        // add markup to columns
        if(count($custom_column) > 0)
        {
            foreach ($custom_column as $key => $column)
            {
                $this->datatables->edit_column($key, $column['markup'], $column['keys']);
            }
        }

        // create checkboxes for each data
        if($checkbox)
        {
            $this->datatables->edit_column($this->primary_key,'<input type="checkbox" name="items[]" class="item" value="$1">', $this->primary_key);
        } else
        {
            $this->datatables->unset_column($this->primary_key);
        }

        // create buttons for data manipulation
        if($options)
        {
            $this->datatables->add_column('options', $this->options_markup, $this->primary_key);
        }

        $this->datatables->from($this->table);
        return $this->datatables->generate();
    }

    /**
     * Retrieves filtered number of records from a database table.
     * @access public
     * @param array $args An array containing filters to the database query.
     * @param string $search_term String used to filter database query.
     * @param int $offset Starting point of the records to be retrieved.
     * @param int $limit Number of records to be retrieved. Default is NULL or all records.
     * @param string $sort The name of the column where the records will be sorted example 'id'.
     * @param string $order Order of the records being retrieved values should either be 'asc' or 'desc'.
     * @return array Returns an array of filtered records.
     */
    function search($args = array(), $search_term = '', $alias = '*',  $limit = NULL, $offset = 0, $sort = NULL, $order = 'asc', $tables = NULL)
    {
        $limit      = $this->limit      === NULL ? $limit               : $this->limit;
        $offset     = $this->offset     === NULL ? $offset              : $this->offset;
        $sort       = $sort             === NULL ? $this->primary_key   : $sort;
        
        // sort and orders
        $orders     = $this->order;
        if( $sort != $this->primary_key )
        {
            $orders[$sort] = $order;
        }
        if( count($orders) > 0 )
        {
            foreach($orders as $key => $ord)
            {
                $this->db->order_by($key, $ord);
            }
        } else
        {
            $this->db->order_by($sort, $order);
        }
        
        $this->db->select($alias, FALSE);
        
        if($tables != NULL)
        {
            foreach ($tables as $key => $table)
            {
                $this->db->join($key, $table);
            }
        }
        
        if (count($args) > 0)
        {
            if(count($args) == 1)
            {
                $this->db->like($args[0], $search_term);
            } else
            {
                $this->db->like($args[0], $search_term);
                for($i = 1; $i < count($args); $i++)
                {
                    $this->db->or_like($args[$i], $search_term);
                }
            }
        }
        
        $query = $this->db->get($this->table, $limit, $offset);
        $this->result = $query->result_array();
        return $this;
    }
    
    /**
     * Retrieves filtered number of records from a database table.
     * @access public
     * @param array $args An array containing filters to the database query.
     * @param array $arr A set of values to use as filter.
     * @param string $column_select The database column to be compared from.
     * @param string $search_term String used to filter database query.
     * @param int $offset Starting point of the records to be retrieved.
     * @param int $limit Number of records to be retrieved. Default is NULL or all records.
     * @param string $sort The name of the column where the records will be sorted example 'id'.
     * @param string $order Order of the records being retrieved values should either be 'asc' or 'desc'.
     * @return array Returns an array of filtered records.
     */
    function search_in($args = array(), $arr = array(), $column_select = 'id', $search_term = '', $alias = '*',  $limit = NULL, $offset = 0, $sort = NULL, $order = 'asc', $tables = NULL)
    {
        $limit          = $this->limit      === NULL ? $limit               : $this->limit;
        $offset         = $this->offset     === NULL ? $offset              : $this->offset;
        $sort           = $sort             === NULL ? $this->primary_key   : $sort;
        $column_select  = $column_select    === NULL ? $this->primary_key   : $column_select;

        if (count($arr) > 0)
        {
            // sort and orders
            $orders = $this->order;
            if( $sort != $this->primary_key )
            {
                $orders[$sort] = $order;
            }
            if( count($orders) > 0 )
            {
                foreach($orders as $key => $ord)
                {
                    $this->db->order_by($key, $ord);
                }
            } else
            {
                $this->db->order_by($sort, $order);
            }
        
            $this->db->select($alias, FALSE);
            
            if($tables != NULL)
            {
                foreach ($tables as $key => $table)
                {
                    $this->db->join($key, $table);
                }
            }
            
            if( count($arr) > 0)
            {
                $this->db->where_in($column_select, $arr);
            }

            if (count($args) > 0)
            {
                if(count($args) == 1)
                {
                    $this->db->where("(" . $this->table .".". $args[0] ." LIKE '%". $search_term . "%')");
                } else
                {
                    $like = "(" . $this->table .".". $args[0] ." LIKE '%". $search_term . "%' ";
                    for($i = 1; $i < count($args); $i++)
                    {
                        $like .= "OR " .$this->table .".". $args[$i] ." LIKE '%". $search_term . "%' ";
                    }
                    $like .= ")";
                    $this->db->where($like);
                }
            }
            
            $query = $this->db->get($this->table, $limit, $offset);
            $this->result = $query->result_array();
            
        } else
        {
            $this->result = array();
        }
        return $this;
    }
    
    /**
     * Counts filtered number of records from a database table.
     * @access public
     * return int Returns number of records within a database table.
     */
    function count($args = array(), $tables = NULL)
    {
        if (count($args) > 0)
        {
            $this->db->where($args);
        }
        
        if($tables != NULL)
        {
            foreach ($tables as $key => $table)
            {
                $this->db->join($key, $table);
            }
        }
        
        $query = $this->db->get($this->table);
        $this->result = $query->num_rows();
        return $this;
    }
    
    /**
     * Counts filtered number of records from a database table.
     * @access public
     * return int Returns number of records within a database table.
     */
    function count_in($args = array(), $arr = array(), $column_select = NULL, $tables = NULL)
    {
        $column_select = $column_select === NULL ? $this->primary_key : $column_select;
        
        if( count($arr) > 0)
        {
            $this->db->where_in($column_select, $arr);
        }
        
        if (count($args) > 0)
        {
            $this->db->where($args);
        }
        
        if(count($arr) > 0)
        {
            $this->db->where_in($column_select, $arr);
        }
        
        if($tables != NULL)
        {
            foreach ($tables as $key => $table)
            {
                $this->db->join($key, $table);
            }
        }
        
        $query = $this->db->get($this->table);
        $this->result = $query->num_rows();
        return $this;
    }
    
    /**
     * Counts filtered/searched number of records from a database table.
     * @access public
     * @param array $args An array containing filters to the database query.
     * @param string $search_term String used to filter database query.
     * @return int Returns number of search records.
     */
    function count_search($args = array(), $search_term = '', $tables = NULL)
    {
        if($tables != NULL)
        {
            foreach ($tables as $key => $table)
            {
                $this->db->join($key, $table);
            }
        }
        
        if (count($args) > 0)
        {
            if(count($args) == 1)
            {
                $this->db->like($args[0], $search_term);
            } else
            {
                $this->db->like($args[0], $search_term);
                for($i = 1; $i < count($args); $i++)
                {
                    $this->db->or_like($args[$i], $search_term);
                }
            }
        }
        
        $query = $this->db->get($this->table);
        $this->result = $query->num_rows();
        return $this;
    }
    
    /**
     * Counts filtered/searched number of records from a database table.
     * @access public
     * @param array $args An array containing filters to the database query.
     * @param string $search_term String used to filter database query.
     * @return int Returns number of search records.
     */
    function count_search_in($args = array(), $arr = array(), $column_select = NULL, $search_term = '', $tables = NULL)
    {
        $column_select = $column_select === NULL ? $this->primary_key : $column_select;
        
        if($tables != NULL)
        {
            foreach ($tables as $key => $table)
            {
                $this->db->join($key, $table);
            }
        }
        
        if( count($arr) > 0 )
        {
            $this->db->where_in($column_select, $arr);
        }
        
        if (count($args) > 0)
        {
            if(count($args) == 1)
            {
                $this->db->where("(" . $this->table .".". $args[0] ." LIKE '%". $search_term . "%')");
            } else
            {
                $like = "(" . $this->table .".". $args[0] ." LIKE '%". $search_term . "%' ";
                for($i = 1; $i < count($args); $i++)
                {
                    $like .= "OR " .$this->table .".". $args[$i] ." LIKE '%". $search_term . "%' ";
                }
                $like .= ")";
                $this->db->where($like);
            }
        }
        
        $query = $this->db->get($this->table);
        $this->result = $query->num_rows();
        return $this;
    }
    
    /**
     * Updates a record from a database table.
     * @access public
     * @param array $data An array containing new information about the record being updated.
     * @param int $id ID of the record to be updated.
     */
    function find_update($id = NULL, $data = NULL)
    {
        if ( is_null($id) || is_null($data) )
        {
            return NULL;
        } else
        {
            $this->db->where($this->primary_key, $id);
            $this->db->update($this->table, $data);
            return $this->db->affected_rows();
        }
    }
    
    /**
     * Updates a record from a database table.
     * @access public
     * @param array $data An array containing new information about the record being updated.
     * @param array $args Arguments to which row/s will be updated.
     */
    function update($args = array(), $data = NULL)
    {
        if ( count($args) > 0 )
        {
            $this->db->where($args);
            $this->db->update($this->table, $data);
            return $this->db->affected_rows();
        } else
        {
            return NULL;
        }
    }
    
    /**
     * Updates multiple records from a database table.
     * @access public
     * @param array $data An array containing new information about the record being updated.
     * @param array $arr Arguments to which row/s will be updated.
     */
    function all_update($arr = array(), $column = NULL, $data = NULL)
    {
        $column = $column === NULL ? $this->primary_key : $column;
        
        if (count($arr) > 0)
        {
            $this->db->where_in($column, $arr);
            $this->db->update($this->table, $data);
            return $this->db->affected_rows();
        } else
        {
            return NULL;
        }
    }
    
    /**
     * Change status to archived. Usually used for records that can't be deleted due to constraints
     * @param array $values Array of values to be deleted
     * @param int $column Databse column from where the values are checked
     * @return string Returns database errors if any
     */
    function archive($values = array(), $column = NULL)
    {
        $column = $column === NULL ? $this->primary_key : $column;
        
        if ( empty($values) )
        {
            return NULL;
        } else
        {
            $archived = array();
            
            foreach ($values as $value)
            {
                $this->db->where($column, $value);
                $this->db->delete($this->table);
                $error_msg = $this->db->_error_message();
                
                // if we cannot delete it, change it's status to archived
                if($error_msg)
                {
                    $this->db->where($column, $value);
                    $this->db->update($this->table, array('status' => 'archived'));
                    array_push($archived, $value);
                    continue;
                }
            }
            return $archived;
        }
    }

    /*
     * Removes a record from a database table.
     * @access public
     * @param array $args Array representing delete parameters.
     * @return string Returns database errors if any
     */

    function delete($args = array())
    {
        if (empty($args))
        {
            return NULL;
        } else
        {
            $this->db->where($args);
            $this->db->delete($this->table);

            $error_msg = $this->db->_error_message();
                
            // if we cannot delete it, change it's status to archived
            if($error_msg)
            {
                $this->db->where($args);
                $this->db->update($this->table, array('status' => 'archived'));
                return 'archived';
            } else
            {
                return 'deleted';
            }
            
        }
    }
    
    /*
     * Removes a record from a database table.
     * @access public
     * @param int id Integer representing the ID of the record that will be deleted.
     * @return string Returns database errors if any
     */
    function find_delete( $id = NULL )
    {
        if ( is_null($id) )
        {
            return NULL;
        } else
        {
            $this->db->where( $this->primary_key, $id);
            $this->db->delete($this->table);

            $error_msg = $this->db->_error_message();
            
            // if we cannot delete it, change it's status to archived
            if($error_msg)
            {
                $this->db->where( $this->primary_key, $id );
                $this->db->update($this->table, array('status' => 'archived'));
                return 'archived';
            } else
            {
                return 'deleted';
            }
        }
    }

    /**
     * Deletes Multiple records according to the specified database column/attribute
     * @access public
     * @param array $values Array of values to be deleted
     * @param int $column Databse attribute/column from where the values are checked
     * @return string Returns database errors if any
     */
    function delete_where($values = array(), $column = NULL)
    {
        $column = $column === NULL ? $this->primary_key : $column;
        
        if ( empty($values) )
        {
            return NULL;
        } else
        {
            $this->db->where_in($column, $values);
            $this->db->delete($this->table);
            return $this->db->_error_message();
        }
    }

    /**
     * Clears a database table.
     * @return string Returns database errors if any
     */
    function all_delete()
    {
        $this->db->delete($this->table);
        return $this->db->_error_message();
    }
    
    // whiskey in the jar
//    public function find_field($field_name)
//    {
//        if ($this->db->field_exists($field_name, $this->table))
//        {
//            return $field_name;
//        } else
//        {
//            return $this->table . '.' . $this->primary_key;
//        }
//    }
    
    /**
     * One to many relationship
     * @param string $table
     * @param string $local_key
     * @param string $parent_key
     * @return \Crud
     */
    function has_many($table, $local_key = 'some_id', $parent_key = 'id')
    {
        if( count($this->result) > 0 )
        {
            if( isset($this->result[0]) && is_array($this->result[0]) )
            {
                $ids = array_column( $this->result, $parent_key );
            } else
            {
                $ids = array( $this->result[ $parent_key ] );
            }

            $this->db->where_in($local_key, $ids);
            $query = $this->db->get($table);

            $this->result = $query->result_array();
        }
        return $this;
    }
    
    /**
     * Return results as array
     * @return array
     */
    function get()
    {
        return $this->result;
    }
    
    /**
     * Return results as array
     * @return array
     */
    function get_array()
    {
        return $this->result;
    }
    
    /**
     * Return results as object
     * @return object
     */
    function get_obj()
    {
        return json_decode(json_encode($this->result), FALSE);
    }
    
    /**
     * Return results as JSON
     * @param string $options json_encode options
     * @return json
     */
    function get_json( $options = NULL )
    {
        if( $options == NULL )
        {
            return json_encode( $this->result );
        } else
        {
            return json_encode( $this->result, $options );
        }
    }
}

/* End of file Crud.php */
/* Location: ./system/application/core/Crud.php */